using System;
using System.Xml;
using PIM.Utilities.Cryptography;
using System.Collections;
using PIM.Utilities.ConfigurationStore;  
using System.Text.RegularExpressions;

namespace PIM.Utilities
{
	
	/// <summary>
	/// This class is used to read and write to the configuration XML,
	/// If any Error occurs while reading the document, Exception is thrown.
	/// </summary>
	/// 
	public class QSecureConfig
	{
		#region PRIVATE CONSTANTS
		
		private string _ProgramFullPath = String.Empty;
		
		private  string _XPATH = "//Application[@Name='{0}']//Key[@name = '{1}']";
		private  string _APPXPATH = "//Application[@Name='{0}']";
		private  string _LOGINPATH="//Login[@Name='{0}']";
		private  string _LOGINXPATH="//Login[@Name='{0}']//pass[@key='{1}']";
		private  string _TAG = "Key";
		private  string _TAGAPP="Name";
		private  string _TAGNAME = "name";
		private  string _TAGVALUE = "value";
		private  string _ISENCRYPT = "isencrypt";
		private XmlDocument m_ConfigXmlDocument = null;
		private QSecureEncrpytion.CryptoAlgo _ENCRYPTALGO = QSecureEncrpytion.CryptoAlgo.TripleDES;
		private string Application = "GLOBAL";
		
		#endregion

		#region STATIC Functions

		//regular expression that has list of invalid characters in xml 
		static Regex reg = new Regex("[\\x00-\\x08|\\x0B-\\x0C|\\x0E-\\x1F]");

		public static ArrayList GetApplicationList()
		{
			ArrayList applist = new ArrayList();
			string FullPath = QSecureRegistry.GetConfigFilePath();

			XmlDocument ConfigXmlDocument = new XmlDocument();
			if( ConfigXmlDocument != null)
			{
				ConfigXmlDocument.Load(FullPath);
			}
			else
				throw new Exception("Unable able to create xmldocument object");

			XmlNodeList nodelist = ConfigXmlDocument.SelectNodes("//Application");
			foreach(XmlNode node in nodelist)
			{
				applist.Add(node.Attributes["Name"].Value);
			}
			
			return applist;
		}

		public static void RemoveSection(string application)
		{
			try
			{
				string FullPath = QSecureRegistry.GetConfigFilePath(); 

				XmlDocument ConfigXmlDocument = new XmlDocument();
				if( ConfigXmlDocument != null)
				{
					ConfigXmlDocument.Load(FullPath);
				}
				else
					throw new Exception("Unable able to create xmldocument object");
				
				string strXPath = String.Format("//Application[@Name='{0}']", application.ToUpper());
				XmlNode Node = ConfigXmlDocument.SelectSingleNode(strXPath);
				if(Node != null)
				{
					Node.RemoveAll();
					ConfigXmlDocument.FirstChild.RemoveChild(Node);	
					// Need to save the document..
					ConfigXmlDocument.Save(FullPath);
					
				}
			}
			catch(Exception)
			{
				//Re-throwing an Exception 
				throw;
			}
		}


		/// <summary>
		/// Replace xml invalid characters with space. 
		/// </summary>
		/// <param name="InputStr"></param>
		/// <returns>converted string</returns>
		public static string ReplaceSpecialCharsWithSpace(string InputStr)
		{
			return ReplaceSpecialChars(InputStr," ");
		}
		/// <summary>
		/// 0x00 to Ox08; 0x0B to 0x0C ; 0x0E to 0x1F are invalid characters in xml. 
		/// </summary>
		/// <param name="InputStr"></param>
		/// <param name="ReplaceStr"></param>
		/// <returns></returns>
		public static string ReplaceSpecialChars(string InputStr, string ReplaceStr)
		{
			return reg.Replace(InputStr, ReplaceStr);
		}

		#endregion

		#region QSecureConfig Implementation

		public QSecureConfig()
		{
			try
			{
				QSecureRegistry.GetConfigFilePath();   
				_ProgramFullPath = QSecureRegistry.GetConfigFilePath(); 
				m_ConfigXmlDocument = new XmlDocument();
				if( m_ConfigXmlDocument != null)
				{
					m_ConfigXmlDocument.Load(_ProgramFullPath);
				}
				else
					throw new Exception("Unable able to create xmldocument object");
			}
			catch (System.Exception err)
			{
                string strMessage = String.Format("Unable to retrieve information, check if configuration xml is vaid in path {0};QSecureConfig();{1}", _ProgramFullPath, err.ToString());
				err.Source=strMessage;
				//throw new Exception(strMessage);
			}

		}

		public QSecureConfig(string Appln)
		{	
			try
			{
				QSecureRegistry.GetConfigFilePath();	
				_ProgramFullPath = QSecureRegistry.GetConfigFilePath(); 
				Application = Appln;

				m_ConfigXmlDocument = new XmlDocument();
				if( m_ConfigXmlDocument != null)
				{
					m_ConfigXmlDocument.Load(_ProgramFullPath);
				}
				else
					throw new Exception("Unable able to create xmldocument object");
			}
			catch (System.Exception  err)
			{
                string strMessage = String.Format("Unable to retrieve information, check if configuration xml is vaid in path {0};QSecureConfig(Appln)-{2};{1}", _ProgramFullPath, err.ToString(), Appln??"null");
				err.Source=strMessage;
			}
		}

		/// <summary>
		/// Retrieves the list of name and value pair values for a given application name. 
		/// </summary>
		/// <param name="KeyTable">out parameter - hashtable of list of name value pairs</param>
		/// <returns></returns>
		public bool GetValues(out Hashtable KeyTable)
		{
			KeyTable = new Hashtable();
			try
			{
				if( m_ConfigXmlDocument == null)
					throw new Exception("Unable able to create xmldocument object");
				
				string strXPath = String.Format(_APPXPATH, Application.ToUpper() );
				XmlNode Node = m_ConfigXmlDocument.SelectSingleNode(strXPath);
				if(Node != null)
				{
					XmlNodeList nodelist = Node.ChildNodes;
					foreach(XmlNode node in nodelist)
					{
						string ky = node.Attributes["name"].Value;
						string val = node.Attributes["value"].Value;
						string encrypted = "";
						if (node.Attributes["isencrypt"] != null )
							encrypted = node.Attributes["isencrypt"].Value;
						else
							encrypted = "false";

						QSecureConfigKeyValue nameval = new QSecureConfigKeyValue(val,encrypted);
						KeyTable.Add(ky,nameval);
					}
				}
				else
				{
                    string strMessage = String.Format("Unable to retrieve information, check if configuration xml is vaid in path {0};GetValues();{1}", _ProgramFullPath, "");
					throw new Exception(strMessage);
				}
			}
			catch(Exception)
			{
				//Re-throwing an Exception 
				throw;
			}		
			return true;
		}
	
		/// <summary>
		/// This method is used to read the Encrypted value for the key with specified name
		/// If key is blank, defualt key is used.
		/// </summary>
		public string GetValue(string Name, string EncryptKey)
		{
			try
			{
				if( m_ConfigXmlDocument == null)
					throw new Exception("Unable able to create xmldocument object");
				
				string strXPath = String.Format(_XPATH, Application.ToUpper(), Name.ToUpper());
				XmlNode Node = m_ConfigXmlDocument.SelectSingleNode(strXPath);
				if(Node != null)
				{
					string strValue = Node.Attributes[_TAGVALUE].Value;
					string sIsEncrypt = Node.Attributes[_ISENCRYPT].Value;
					// If Key is blank use default
					if(EncryptKey == null || EncryptKey.Length == 0)
					{
						//check if isencrypt is set
						if ( sIsEncrypt.ToUpper().Equals("TRUE") )
						{
							QSecureEncrpytion objEnc = new QSecureEncrpytion(_ENCRYPTALGO);
							strValue = objEnc.DecryptString(strValue);
						}

					}
					else
					{
						// Use the incoming key to decrypt the message...
						//check if isencrypt is set
						if ( sIsEncrypt.ToUpper().Equals("TRUE") )
						{
							QSecureEncrpytion objEnc = new QSecureEncrpytion(_ENCRYPTALGO);
							strValue = objEnc.DecryptString(strValue, _ENCRYPTALGO, EncryptKey);
						}
					}
					return strValue;
				}
				else
				{
                    string strMessage = String.Format("Unable to retrieve information, check if configuration xml is vaid in path {0};GetValue(name,encryptkey)-{2};{1}", _ProgramFullPath, "", Name??"null");
					throw new Exception(strMessage);
				}
			}
			catch(Exception)
			{
				//Re-throwing an Exception 
				throw;
			}
		}



		/// <summary>
		/// This method is used to read the value for the key with specified name
		/// </summary>
		/// 
		public string GetValue(string Name )
		{
			try
			{
				if( m_ConfigXmlDocument == null)
					throw new Exception("Unable able to create xmldocument object");
				
				string strXPath = String.Format(_XPATH, Application.ToUpper(), Name.ToUpper());
				XmlNode Node = m_ConfigXmlDocument.SelectSingleNode(strXPath);
				if(Node != null)
				{
					return Node.Attributes[_TAGVALUE].Value;
				}
				else
				{
                    string strMessage = String.Format("Unable to retrieve information, check if configuration xml is vaid in path {0};GetValue(name)-{2};{1}", _ProgramFullPath, "", Name ?? "null");
					throw new Exception(strMessage);
				}
			}
			catch(System.Exception ex)
			{
				//Re-throwing an Exception 
                string strMessage = String.Format("Unable to retrieve information, check if configuration xml is vaid in path {0};GetValue(name)-{2};{1}", _ProgramFullPath, "", Name ?? "null");
				throw new Exception(strMessage, ex);
				//throw;
			}
		}
		/// <summary>
		/// This method is used to read the value for the Loginkey with specified name
		/// </summary>
		/// 
		public string GetPWD(string Name)
		{
			try
			{
				if( m_ConfigXmlDocument == null)
					throw new Exception("Unable able to create xmldocument object");
				string strValue=string.Empty; 
				string strXPath = String.Format(_LOGINPATH,Application.ToUpper());
				XmlNode Node = m_ConfigXmlDocument.SelectSingleNode(strXPath);
				if(Node!=null)
				{
					strXPath=String.Format(_LOGINXPATH,Application.ToUpper(),Name.ToUpper());  
					XmlNode NodePath =m_ConfigXmlDocument.SelectSingleNode(strXPath);
					if (NodePath!=null) 
					{
						strValue =NodePath.Attributes["value"].Value;   
					}
					return strValue;
				}
				else
				{
                    string strMessage = String.Format("Unable to retrieve information, check if configuration xml is vaid in path {0};GetPWD(name)-{2};{1}", _ProgramFullPath, "", Name ?? "null");
					throw new Exception(strMessage);
										
				}
			}
			catch(System.Exception ex)
			{
				//Re-throwing an Exception 
                string strMessage = String.Format("Unable to retrieve information, check if configuration xml is vaid in path {0};GetPWD(name)-{2};{1}", _ProgramFullPath, "", Name ?? "null");
				throw new Exception(strMessage, ex);
				//throw;
			}
		}
		///<symmary>
		/// This method is used to set the NewPassword.
		///</symmary>
		public bool SetPwd(string Name,string newPwd)
		{
			try
			{
				if( m_ConfigXmlDocument == null)
					throw new Exception("Unable able to create xmldocument object");
				string strValue=string.Empty; 
				string strXPath = String.Format(_LOGINPATH,Application.ToUpper());
				XmlNode Node = m_ConfigXmlDocument.SelectSingleNode(strXPath);
				if(Node!=null)
				{
					strXPath=String.Format(_LOGINXPATH,Application.ToUpper(),Name.ToUpper());  
					XmlNode NodePath =m_ConfigXmlDocument.SelectSingleNode(strXPath);
					if (NodePath!=null) 
					{
						QSecureEncrpytion objEnc = new QSecureEncrpytion(_ENCRYPTALGO);
						strValue=objEnc.EncryptString(newPwd);
						 
						((XmlElement) NodePath).SetAttribute("value", strValue);
						m_ConfigXmlDocument.Save(_ProgramFullPath);
					}
					return true;
				}
				else
				{
					return false;
					//string strMessage = String.Format("Unable to retrieve information, check if configuration xml is vaid in path {0}", _ProgramFullPath);
					//throw new Exception(strMessage);
																		
				}
			}
			catch
			{
				//Re-throwing an Exception 
				return false ;
			}
		}
		/// <summary>
		/// This method is used to Set a list of values. 
		/// </summary>
		public void SetValues(Hashtable KeyList)
		{
			try
			{
				if( m_ConfigXmlDocument == null)
					throw new Exception("Unable able to create xmldocument object");
				
				string strXPath = String.Format(_APPXPATH, Application.ToUpper() );
				
				XmlNode Node = m_ConfigXmlDocument.SelectSingleNode(strXPath);
				if(Node != null)
				{
					Node.RemoveAll();					
				}
				else
				{
					// Create Application node.
					XmlElement newAppElem = m_ConfigXmlDocument.CreateElement("Application");
					m_ConfigXmlDocument.DocumentElement.AppendChild(newAppElem);
					Node = newAppElem;
				}

				foreach(DictionaryEntry e in KeyList)
				{
					QSecureConfigKeyValue kyval = (QSecureConfigKeyValue ) e.Value;
					XmlElement newKeyElem = CreateKeyElement(e.Key.ToString(),kyval.Value, kyval.IsEncrypt);
					Node.AppendChild(newKeyElem);
				}
				
				((XmlElement) Node).SetAttribute("Name", Application.ToUpper());				
				
				m_ConfigXmlDocument.Save(_ProgramFullPath);

			}
			catch(Exception)
			{
				//Re-throwing an Exception 
				throw;
			}
		}

		public void SetValue(string Name, string Value)
		{
			try
			{
				if( m_ConfigXmlDocument == null)
					throw new Exception("Unable able to create xmldocument object");
				
				string strXPath = String.Format(_XPATH, Application.ToUpper(), Name.ToUpper());
				XmlNode Node = m_ConfigXmlDocument.SelectSingleNode(strXPath);

				if(Node != null)
				{
					Node.Attributes[_TAGVALUE].Value = Value;
					// Need to save the document..
					m_ConfigXmlDocument.Save(_ProgramFullPath);
					
				}
				else
				{
					// Create a node, 
					CreateNode(Name, Value, "false");

					// Need to save the document..
					m_ConfigXmlDocument.Save(_ProgramFullPath);					
				}
			}
			catch(Exception)
			{
				//Re-throwing an Exception 
				throw;
			}
		}

		/// <summary>
		/// This method is used to Set value for the key with specified Name
		/// </summary>
		public void SetValue(string Name, string Value, string Isencrypt)
		{
			try
			{
				QSecureConfigKeyValue keyvalue = new QSecureConfigKeyValue();
				if( m_ConfigXmlDocument == null)
					throw new Exception("Unable able to create xmldocument object");
				if (Isencrypt=="true")
				{
					//string Value = "";
					// encrypt if checked. 
					QSecureEncrpytion objEnc = new QSecureEncrpytion(QSecureEncrpytion.CryptoAlgo.TripleDES);
					Value = objEnc.EncryptString(Value);
					keyvalue.Value = Value;
					keyvalue.IsEncrypt = "true";
				}
				else
				{
					keyvalue.Value=Value;
					keyvalue.IsEncrypt = "false";
				}
				//string strXPath = String.Format(_XPATH, Application.ToUpper(), Name.ToUpper());
				string strXPath = String.Format(_APPXPATH, Application.ToUpper() );
				XmlNode Node = m_ConfigXmlDocument.SelectSingleNode(strXPath);
				//XmlNode Node = m_ConfigXmlDocument.SelectSingleNode(strXPath);
				if(Node != null)
				{
					strXPath = String.Format(_XPATH, Application.ToUpper(), Name.ToUpper());
					XmlNode NodeParam = m_ConfigXmlDocument.SelectSingleNode(strXPath);

					if (NodeParam != null)
					{
						Node.RemoveChild(NodeParam);
					}
					XmlElement newKeyElem = CreateKeyElement(Name, keyvalue.Value, keyvalue.IsEncrypt);
					Node.AppendChild(newKeyElem);
					m_ConfigXmlDocument.Save(_ProgramFullPath);
				}
				else
				{
					// Create a node,
					CreateNode(Name,Value,Isencrypt);
					// Need to save the document..
					m_ConfigXmlDocument.Save(_ProgramFullPath);
					
				}
			}
			catch(Exception)
			{
				//Re-throwing an Exception 
				throw;
			}
		}

		/// <summary>
		/// This method is used to Set the Encrypted value for the key with specified name
		/// If Key is blank, defult key is used.
		/// </summary>
		public void SetValue(string Name, string Value,string Isencrypt, string EncryptKey)
		{
			QSecureConfigKeyValue keyvalue = new QSecureConfigKeyValue();
			try
			{
				if( m_ConfigXmlDocument == null)
					throw new Exception("Unable able to create xmldocument object");
				
				// If Key is blank use default
				
				if (Isencrypt=="true")
				{
					if(EncryptKey == null || EncryptKey.Length == 0)
					{
						QSecureEncrpytion objEnc = new QSecureEncrpytion(_ENCRYPTALGO);
						Value = objEnc.EncryptString(Value);
					}
					else
					{
						// Use the incoming key to Encrypt the message...
						QSecureEncrpytion objEnc = new QSecureEncrpytion(_ENCRYPTALGO);
						Value = objEnc.EncryptString(Value, _ENCRYPTALGO, EncryptKey);
					}
					keyvalue.Value=Value;
					keyvalue.IsEncrypt="true"; 

				}
				else
				{
					keyvalue.Value=Value;
					keyvalue.IsEncrypt="false"; 

				}
				//Commented for the moment by sashi
				string strXPath = String.Format(_APPXPATH,Application.ToUpper());
				//Comment ends here
				XmlNode Node = m_ConfigXmlDocument.SelectSingleNode(strXPath);
				if(Node != null)
				{
					strXPath = String.Format(_XPATH, Application.ToUpper(), Name.ToUpper());
					XmlNode NodeParam = m_ConfigXmlDocument.SelectSingleNode(strXPath);

					if (NodeParam != null)
					{
						Node.RemoveChild(NodeParam);
					}

					XmlElement newKeyElem = CreateKeyElement(Name, keyvalue.Value, keyvalue.IsEncrypt);
					Node.AppendChild(newKeyElem);
					
					m_ConfigXmlDocument.Save(_ProgramFullPath);
				}
				else
				{
					// Create a node,
					CreateNode(Name,Value,Isencrypt);
					// Need to save the document..
					m_ConfigXmlDocument.Save(_ProgramFullPath);
					
				}

			}
			catch(Exception)
			{
				//Re-throwing an Exception 
				throw;
			}
		}

		public void RemoveValue(string Name)
		{
			try
			{
				if( m_ConfigXmlDocument == null)
					throw new Exception("Unable able to create xmldocument object");
				
				string strXPath = String.Format(_XPATH, Application.ToUpper(), Name.ToUpper());
				string strParentXPath = String.Format(_APPXPATH, Application.ToUpper());
				XmlNode Node = m_ConfigXmlDocument.SelectSingleNode(strXPath);
				XmlNode ParentNode = m_ConfigXmlDocument.SelectSingleNode(strParentXPath);
				if(Node != null && ParentNode != null)
				{
					ParentNode.RemoveChild(Node);
					m_ConfigXmlDocument.Save(_ProgramFullPath);
				}
				else
				{
                    string strMessage = String.Format("Unable to retrieve information, check if configuration xml is vaid in path {0};RemoveValue(name)-{2};{1}", _ProgramFullPath, "", Name ?? "null");
					throw new Exception(strMessage);
				}
			}
			catch(Exception)
			{
				//Re-throwing an Exception 
				throw;
			}

		}


		#endregion

		#region PRIVATE MEMBERS 

		private void CreateNode(string Name, string Value,string IsEncrypt)
		{
			try 
			{
				string strAppXPath = String.Format(_APPXPATH,Application.ToUpper());
				XmlNode AppNode = m_ConfigXmlDocument.SelectSingleNode(strAppXPath);
				if ( AppNode != null )
				{
					XmlElement newKeyElem = CreateKeyElement(Name,Value,IsEncrypt);
					AppNode.AppendChild(newKeyElem);
				}
				else
				{
					XmlElement newAppElem = CreateAppElement();
					XmlElement newElem = CreateKeyElement(Name,Value,IsEncrypt);
					newAppElem.AppendChild(newElem);
					m_ConfigXmlDocument.DocumentElement.AppendChild(newAppElem);
				}
			}
			catch
            {
                throw;
			}

		}

		private XmlElement CreateAppElement()
		{
			XmlElement newElem = m_ConfigXmlDocument.CreateElement("Application");
			newElem.SetAttribute(_TAGAPP, Application.ToUpper());
			return newElem;
		}

		private XmlElement CreateKeyElement(string Name, string Value, string IsEncrypt)
		{
			XmlElement newElem = m_ConfigXmlDocument.CreateElement(_TAG);
			newElem.SetAttribute(_TAGNAME, Name.ToUpper());
			newElem.SetAttribute(_TAGVALUE, Value);
			newElem.SetAttribute(_ISENCRYPT, IsEncrypt);
			return newElem;
		}

		
		#endregion
	};

	public class QSecureConfigKeyValue
	{
		private string _Value;
		private string _IsEncrypt = "false";
		
		public QSecureConfigKeyValue()
		{
			_Value = "";
		}

		public QSecureConfigKeyValue(string val)
		{
			_Value = val;
		}

		public QSecureConfigKeyValue(string val, string isencrypt)
		{
			_Value = val;
			if (isencrypt == string.Empty || isencrypt == null || isencrypt == "")
				_IsEncrypt = "false";
			else
				_IsEncrypt = isencrypt;
		}

		public string Value
		{
			get
			{
				return _Value;
			}
			set
			{
				_Value = value;
			}
		}

		public string IsEncrypt
		{
			get
			{
				return _IsEncrypt;
			}
			set
			{
				_IsEncrypt = value;
			}
		}

		
	}	
}
